import java.util.*;
/** @brief representacio del tauler a on es duran a terme les operacions,colocar unitats, moure-les i totes les altres posibilitas*/
public class Mapa{
	
	private TreeMap<String,Territori> territoris;/** @var conjunt de territoris existents */
	private TreeMap<String, Enclavament>enclavaments;/** @var conjunt de enclavaments existents*/
	private Integer puntMax;/** @var puntacio maxima per guanyar la partida*/
	private double millorDistancia;/** @var  variable privada per fer el backtrakcing indica la distancia mes curta a on fer un exili.*/
	private String solucio;/** @var variable privada per fer el backtracking indica el municipi d'exili. */
	
	/**
	 * @pre: --
	 * @post mostra el nom de tots els territoris
	 */
	public void mostrarTerritoris(){
		for(Map.Entry<String,Territori> entry : territoris.entrySet()) { 
			Territori value = entry.getValue();
			System.out.println(value.ObtenirNom());
		}
		
	}
	
	
	/**@pre:--
	@post: Constructor per defecte */
	public Mapa()
	{
		territoris = new TreeMap<String,Territori>();
		enclavaments = new TreeMap<String,Enclavament>();
		puntMax=0;
	}
	
	/**@pre:--
	@post: afegeix el territori interritori al treemap territoris */
	public void AfegirTerritori(Territori inTerritori)
	{
		territoris.put(inTerritori.ObtenirNom(),inTerritori);
	}
	
	/**@pre:--
	@post: afegeix l'enclavament nomEncl al treemap territoris */
	public void AfegirEncl(Enclavament nomEncl)
	{
		enclavaments.put(nomEncl.obtNom(),nomEncl);
	}
	
	/**@pre:--
	 * @post: retorna el mapa d'enclavaments
	 */
	public TreeMap<String, Enclavament> ObtLlistaEnclavament()
	{
		return enclavaments;
	}
	
	/**@pre:--
	@post: Busca el territori nomTerritori al map territoris, si no hi es retorna null */
	public Territori ObtTerritori(String nomTerritori){
		Territori t=territoris.get(nomTerritori);
		return t;
	}
	
	/**@pre:--
		@post: retorna cert si el territori existeix al treemapa territoris
	 */
	public boolean TerritoriExisteix(String nomTerritori){
		return (territoris.containsKey(nomTerritori));
	}
	
	
	/**@pre:--
	@post: retorna cert si l'enclavament existeix al treemapa enclavaments*/
	public boolean EnclavamentExisteix(String nomEncl){
		return (enclavaments.containsKey(nomEncl));
	}
	
	/**@pre:--
	@post: Busca l'enclavament nomEnclavament al map d'enclavaments, si no hi es retorna null */
	public Enclavament ObtEnclavament(String nomEnclavament){
		Enclavament e = enclavaments.get(nomEnclavament);
		return e;
	}

	
	/**@pre:
	  @post: Busca enclavaments sense ocupar i li assigna civils de totes les etnies de la partida
	 */
	public void InicialitzarEnclavamentsNoOcupats(TreeMap<String, Etnia> etnies){
		for(Map.Entry<String,Enclavament> entry : enclavaments.entrySet()) { //pasa por la llista de etnies
			Enclavament value = entry.getValue();
			if(!value.ocupat()){
				for(Map.Entry<String,Etnia> entry2 : etnies.entrySet()) {
					Etnia value2 = entry2.getValue();
					value.InicialitzarUnitats(value2,"pob");;
				}
			}
		}
	}
	
	
	  /**@pre: punt>0
	  @post: Posa punt com a puntMax */
	public void afegirPuntuacioMaxima(int punt){
		puntMax=punt;
	}
	
	
	 /**
	   * @pre:
	   * @post: Calcula els punts del joc, els mostra per pantalla i 
	  retorna true en cas que alguna Etnia de etnies ha arribat als puntMax. */
	public boolean CalcularIMostrarPunts(TreeMap<String,Etnia> etnies)
	{
		boolean finalitzat = false;
		Map<String,Integer> PuntuacioEtnies = new TreeMap();
		Iterator iteratorEtnies = etnies.entrySet().iterator();
		while (iteratorEtnies.hasNext())
		{
			Map.Entry<String,Integer> mapEntry = (Map.Entry) iteratorEtnies.next();
			PuntuacioEtnies.put(mapEntry.getKey(), 0);
		}
		
		Iterator iteratorEnclavaments = enclavaments.entrySet().iterator();
		int valor = 0 ;
		while (iteratorEnclavaments.hasNext())
		{
			Map.Entry<String, Enclavament> mapEntryEnclavaments = (Map.Entry<String, Enclavament>)iteratorEnclavaments.next();
			Enclavament nomEnclavament = mapEntryEnclavaments.getValue();
			Integer valorEnclavament = mapEntryEnclavaments.getValue().Obtvalor();
			valorEnclavament = valorEnclavament * Multiplicitat(nomEnclavament);
			
			if(nomEnclavament.ocupatMilitartment())
			PuntuacioEtnies.put(nomEnclavament.ocupa().nom(),PuntuacioEtnies.get(nomEnclavament.ocupa().nom())+valorEnclavament);
		}
		
		iteratorEtnies = PuntuacioEtnies.entrySet().iterator();
		System.out.println("CLASSIFICACIO: ");
		while (iteratorEtnies.hasNext())
		{
			Map.Entry<String,Integer> mapEntry = (Map.Entry) iteratorEtnies.next();
			System.out.println("Etnia: "+mapEntry.getKey()+"  Puntuacio: "+mapEntry.getValue());
			if (mapEntry.getValue() >= puntMax)
			{
				finalitzat = true;
				System.out.println(mapEntry.getKey()+ " ha gunyat");
			}
		}
		return finalitzat;
	}
	
	  /**@pre:-
	  @post: retorna el numero per el que han de multiplicarse el valor de e. Segons el nivells de territoris en posesio */
	public Integer Multiplicitat(Enclavament e)
	{
		Integer returnValue =1;
		boolean sortir = false;
		String aux = e.pertanyAlTerritori();
		Territori t = territoris.get(aux);
		
		if (e.ocupatMilitartment())
		{
			while (t!=null && !sortir)
			{
				if (estaColonitzat(t,e.ocupa()))
				{ returnValue*=2;}
				else
					sortir = true;
				t= territoris.get(t.esSubterritoride());
			}
			if (e.hiHaCivilsEtnia(e.ocupa().nom()))
			{
				returnValue *=2;
			}
		}
		return returnValue;
	}
	
	/**
	 * @pre existeix territori
	 * @post retorna si territori T es colonitzat per la misma etnia*/
	public boolean estaColonitzat(Territori t,Etnia e)
	{
		List<String> subterritoris ;
		List<String> enclavamentsTerritoris;
		subterritoris = t.Obtsubterritoris();
		Iterator itSubterritoris = subterritoris.iterator();
		boolean b = true;
		
		while (itSubterritoris.hasNext() && b ==true)
		{
			b = estaColonitzat(territoris.get(itSubterritoris.next()),e);	
		}
		boolean sortir = false;
		if (b)
		{
			enclavamentsTerritoris = t.ObtEnclavaments();
			Iterator itEnclavaments = enclavamentsTerritoris.iterator();
			while(itEnclavaments.hasNext() && !sortir )
			{
				Enclavament enclavamentActual = enclavaments.get(itEnclavaments.next());
				if((!enclavamentActual.ocupatMilitartment()) || (enclavamentActual.ocupatMilitartment() && enclavamentActual.ocupa() != e))
					sortir = true;
			}
		}
		return ( b && !sortir);
	}
	
	
	/**
	 * @pre: 
	 * @post: retorna el nom del territori al qual pertany e en cas de no trobar retorna null */
	public void Exili(Enclavament e, Etnia et)
	{
		solucio = "";
		Double dist = 0.0;
		millorDistancia = 0.0;
		Backtracking(e,et,dist);
		if (millorDistancia != 0.0)
		{
			System.out.print("Els civils s'han exiliat a: "+solucio);
			System.out.println(" Amb una distancia de: "+ millorDistancia);
			Municipi mun = (Municipi) enclavaments.get(solucio);
			mun.afegirCivils(et.nom());
		}
		else
			System.out.println("Els civils de "+et.nom()+" no han pogut exiliarse");
		Municipi aux = (Municipi) e;
		aux.TreureCivils(et.nom());
	}
	
	/**
	 * @pre: valors e,et,millorDistancia,dist,solucio han d'entrar amb els valors adequats.
	 * @post: Backtracking que resol el problema de trobar el municipi al cual exiliarse mes proper.*/
	private void Backtracking(Enclavament e,Etnia et,Double dist)
	{
		List<String> visitats = new ArrayList();
		Iterator camins = e.Obtcamins().iterator();
		Iterator carreteres = e.Obtcarreteres().iterator();
		
		while (camins.hasNext() || carreteres.hasNext())
		{
			if (camins.hasNext())
			{
				String nomEnclavament = (String) camins.next();
				if (!visitats.contains(nomEnclavament))
				{
					if(!enclavaments.get(nomEnclavament).ocupatMilitartment() 
							|| (enclavaments.get(nomEnclavament).ocupa()!=null&& enclavaments.get(nomEnclavament).ocupa().equals(et)))
					{
						visitats.add(nomEnclavament);
						dist = dist + e.CalcularDistancia(enclavaments.get(nomEnclavament));
						if(!enclavaments.get(nomEnclavament).potExiliarse(et))
						{
							Backtracking(enclavaments.get(nomEnclavament),et,dist);
						}
						else
						{
							if (dist < millorDistancia || millorDistancia==0)
							{
								millorDistancia = dist;
								solucio = nomEnclavament;
							}
						}
						visitats.remove(nomEnclavament);
						dist = dist - e.CalcularDistancia(enclavaments.get(nomEnclavament));
					}
				}
			}
			else //carreteres 
			{
				String nomEnclavament = (String) carreteres.next();
				if (!visitats.contains(nomEnclavament))
				{
					if(!enclavaments.get(nomEnclavament).ocupatMilitartment() 
							|| (enclavaments.get(nomEnclavament).ocupa()!=null&& enclavaments.get(nomEnclavament).ocupa().equals(et)))
					{
						visitats.add(nomEnclavament);
						dist = dist + e.CalcularDistancia(enclavaments.get(nomEnclavament));
						if(!enclavaments.get(nomEnclavament).potExiliarse(et))
						{
							Backtracking(enclavaments.get(nomEnclavament),et,dist);
						}
						else
						{
							if (dist < millorDistancia || millorDistancia==0)
							{
								millorDistancia = dist;
								solucio = nomEnclavament;
							}
						}
						visitats.remove(nomEnclavament);
						dist = dist - e.CalcularDistancia(enclavaments.get(nomEnclavament));
					}
				}
				
			}
		}
	}
	
}